/**
 *  Serponix is an arcade game in focus to multiplayer based on the classic game Snake.
 *  Copyright (C) 2010 - 2011  Daniel Vala
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License,
 *  or  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.

 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  If you have any question do not hesitate to contact author
 *  on e-mail address: danielvala42@gmail.com
 */
package com.serponix.game;

import com.serponix.game.objektyNaPlose.Zed;
import com.serponix.settings.SettingsFactory;
import java.awt.Color;
import java.awt.Graphics;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.swing.JOptionPane;
import com.serponix.net.Informace;

/**
 * Mapa obsahuje zdi, které mohou být ve hře umístěny.
 * Lze přidat novou zeď nebo ji smazat,
 * poslat informace pro kreslení mapy
 * a uložit ji do souboru či ji ze souboru načíst.
 * Mapu lze také vykreslit s texturou či bez textury.
 * @author Daniel Vala
 */
public class Mapa {

    private static final String PATH;
    private static final String ODDELOVAC1 = "$";
    private static final String ODDELOVAC2 = "|";
    private List<Zed> mapa;
    private String nazev;

    static {
        PATH = SettingsFactory.getDirPath() + "mapy" + File.separator;
    }

    /**
     * Vytvoří prázdnou mapu.
     */
    public Mapa() {
        mapa = new ArrayList<Zed>();
        nazev = "prázdná mapa";
    }

    /**
     * Vytvoří mapu dle zadané informace pro kreslení.
     * @param m Informace pro kreslení
     */
    public Mapa(String m) {
        mapa = new ArrayList<Zed>();
        StringTokenizer st = new StringTokenizer(m, ODDELOVAC1);
        while (st.hasMoreTokens()) {
            String souradnice = st.nextToken();
            StringTokenizer st2 = new StringTokenizer(souradnice, ODDELOVAC2);
            mapa.add(new Zed(Integer.parseInt(st2.nextToken()), Integer.parseInt(st2.nextToken())));
        }
    }

    public void namapujCelouMapu(GameModel model) {
        for (Zed zed : mapa) {
            zed.namapuj(model);
        }
    }

    /**
     * Vytvoří informace pro kreslení podle všech souřadnic zdí.
     * Podle tohoto Stringu lze vytvořit novou mapu.
     * @return Informace pro kreslení celé mapy.
     */
    public String getInformaceProKresleni() {
        if (isEmpty()) {
            return "";
        }
        String instrukce = Informace.MAPA + Informace.ODDELOVAC3;
        for (Zed zed : mapa) {
            instrukce += zed.getX() + ODDELOVAC2 + zed.getY() + ODDELOVAC1;
        }
        return instrukce;
    }

    /**
     * Vrátí název mapy.
     * @return Název mapy.
     */
    public String getNazev() {
        return nazev;
    }

    /**
     * Vrátí cestu k adresáři, kde jsou uložené všechny mapy.
     * Pokud cesta neexistuje, nejdříve ji vytvoří.
     * @return Cesta k adresáři s mapami.
     */
    public static String getPath() {
        new File(PATH).mkdirs();
        return PATH;
    }

    /**
     * Zjistí, zda mapa obsahuje alespoň jednu zeď, nebo je prázdná.
     * @return True, pokud je mapa prázdná. False, pokud obsahuje alespoň jednu zeď.
     */
    public boolean isEmpty() {
        return mapa.isEmpty();
    }

    /**
     * Přidá do mapy zeď s danými souřadnicemi.
     * Pokud na daných souřadnicích zeď již existuje, nepřidá se.
     * @param x X souřadnice zdi.
     * @param y Y souřadnice zdi.
     */
    public void pridejZed(int x, int y) {
        if (!existujeZed(x, y)) {
            mapa.add(new Zed(x, y));
        }
    }

    /**
     * Smaže zeď na zadaných souřadnicích, pokud existuje.
     * @param x Souřadnice zdi X.
     * @param y Souřadnice zdi Y.
     * @return True, pokud na daných souřadnicích byla zeď, která se smazala. Jinak false.
     */
    public boolean smazZed(int x, int y) {
        for (Zed zed : mapa) {
            if (zed.getX() == x && zed.getY() == y) {
                mapa.remove(zed);
                return true;
            }
        }
        return false;
    }

    public void smazZed(GameModel model, Zed zed) {
        zed.odmapuj(model);
        mapa.remove(zed);
    }

    /**
     * Smaže všechny zdi mapy.
     */
    public void smazCelouMapu() {
        mapa.clear();
    }

    /**
     * Zkontroluje, zda daná souřadnice je obsažena jako stěna v mapě.
     * @param x Souřadnice x
     * @param y Souřadnice y
     * @return true, pokud je obsažena, jinak false.
     */
    public boolean existujeZed(int x, int y) {
        for (int i = 0; i < mapa.size(); i++) {
            if (x == mapa.get(i).getX() && y == mapa.get(i).getY()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Vykreslí všechny zdi mapy.
     * @param g2d Grafický objekt ke kreslení.
     */
    public void kresliMapu(Graphics g) {
        for (Zed zed : mapa) {
            zed.draw(g);
        }
    }

    /**
     * Vykreslí všechny zdi mapy jen černě, bez jejich textur.
     * @param g2d Grafický objekt ke kreslení.
     */
    public void kresliMapuBezTextur(Graphics g) {
        for (Zed zed : mapa) {
            g.setColor(Color.BLACK);
            g.fillRect(zed.getX(), zed.getY(), SettingsFactory.velikostPolicka, SettingsFactory.velikostPolicka);
        }
    }

    /**
     * Načte mapu ze zadaného InputStreamu.
     * @param nazevMapy Název mapy.
     * @param stream InputStream, ze kterého se načte mapa.
     */
    @SuppressWarnings("unchecked")
    public void nactiMapu(String nazevMapy, InputStream stream) {
        ObjectInputStream input = null;
        try {
            input = new ObjectInputStream(stream);
            mapa = (List<Zed>) input.readObject();
            this.nazev = nazevMapy;
        } catch (FileNotFoundException ex) {
            JOptionPane.showMessageDialog(null, "Soubor mapy nebyl nalezen.", "Chyba", JOptionPane.ERROR_MESSAGE);
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, "Chyba čtení souboru. Tato mapa nebyla vytvořena v editoru této hry.", "Chyba", JOptionPane.ERROR_MESSAGE);
            System.exit(1);
        } catch (ClassNotFoundException ex) {
            JOptionPane.showMessageDialog(null, "Chyba při čtení souboru mapy.", "Chyba", JOptionPane.ERROR_MESSAGE);
        } finally {
            try {
                input.close();
            } catch (IOException ex) {
                JOptionPane.showMessageDialog(null, "Chyba při zavírání souboru mapy.", "Chyba", JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    /**
     * Uloží mapu do souboru pod zadaným názvem.
     * @param nazevMapy Soubor bude pojmenován podle názvu mapy + koncovka ser.
     * @return True, pokud se mapu podařilo uložit. Jinak false.
     */
    public boolean ulozMapu(String nazevMapy) {
        nazevMapy += ".ser";
        if (new File(PATH + nazevMapy).exists()) {
            Object[] options = {"Ano", "Ne"};
            int vyber = JOptionPane.showOptionDialog(null,
                    "Mapa se zadaným názvem již existuje. \n Chcete ji přepsat?", "Chyba",
                    JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE, null,
                    options, options[1]);
            if (vyber != 0) {
                return false;
            }
        }
        ObjectOutputStream output = null;
        try {
            new File(PATH).mkdirs();
            output = new ObjectOutputStream(new FileOutputStream(new File(PATH + nazevMapy)));
            output.writeObject(mapa);
            this.nazev = nazevMapy;
        } catch (FileNotFoundException ex) {
            JOptionPane.showMessageDialog(null, "Ověřte, že máte do oprávnění do složky " + PATH, "Chyba", JOptionPane.ERROR_MESSAGE);
            return false;
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, "Ověřte, že máte do oprávnění do složky " + PATH, "Chyba", JOptionPane.ERROR_MESSAGE);
            return false;
        } finally {
            try {
                if (output != null) {
                    output.close();
                }
            } catch (IOException ex) {
                JOptionPane.showMessageDialog(null, "Chyba při zavírání souboru mapy.", "Chyba", JOptionPane.ERROR_MESSAGE);
            }
        }
        return true;
    }

    @Override
    public String toString() {
        return nazev;
    }
}
